/*
 * A part of the Faun project
 *
 * This grammar originates from http://code.google.com/p/phpparser/
 * 
 * BSD license
 * 
 * Copyright (c) 2009 by Yauhen Yakimovich 
 * Copyright (c) 2009 by Sidharth Kuruvila
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY HABELITZ SOFTWARE DEVELOPMENTS ('HSD') ``AS IS'' 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL 'HSD' BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
tree grammar PhpRecognizer; 
options { 
tokenVocab=Php;
ASTLabelType=CommonTree;
backtrack=true;
output=AST;
}

@header{
    package org.faun.recognizer;
    
    import org.faun.recognizer.statement.*;
}

source returns[PhpSource value]
    : statement*;

statement returns[PhpStatement value]
    : BodyString
    | ^(Block (smts+=statement)*) { $value = (PhpStatement) new BlockStatement($smts);}
    //| UnquotedString Colon statement -> ^(Label UnquotedString statement)
    | classDefinition     {$value = $classDefinition.value;}
    | interfaceDefinition {$value = $interfaceDefinition.value;}
    | complexStatement    {$value = $complexStatement.value;}
    | simpleStatement     {$value = $simpleStatement.value;}
    ;

interfaceDefinition returns[PhpStatement value]
    : ^(Interface UnquotedString interfaceExtends? interfaceMember*)
    ;

interfaceExtends
    : Extends^ UnquotedString+
    ;
interfaceMember
    : ^(Const UnquotedString atom?)
    | ^(Method ^(Modifiers fieldModifier*) UnquotedString ^(Params paramDef*))
    ;

classDefinition returns[PhpStatement value]
    :   ^(Class ^(Modifiers classModifier?) UnquotedString (^(Extends UnquotedString))? classImplements? classMember*)
    ;
    
classImplements
    :  ^(Implements UnquotedString+)
    ;

classMember
    : ^(Method ^(Modifiers fieldModifier*) UnquotedString ^(Params paramDef*) statementBlock?)
    | ^(Var ^(Dollar UnquotedString) atom?) 
    | ^(Const UnquotedString atom?)
    | ^(Field ^(Modifiers fieldModifier*) ^(Dollar UnquotedString) atom?)
    ;
    
statementBlock
    : ^(Block statement*)
    ;

fieldDefinition
    : ^(Field ^(Dollar UnquotedString) atom?)
    ;
    
classModifier
    : 'abstract';
    
fieldModifier
    : AccessModifier | 'abstract' | 'static' 
    ;


complexStatement returns[PhpStatement value]
    : ^('if' expression smt=statement els=statement?) 
      { $value = new IfStatement($expression.value, $smt.value, $els.value);}
    | ^(For forInit forCondition forUpdate statement)
      { $value = new ForStatement($forInit.value, $forCondition.value, $forUpdate.value, $statement.value);}
    | ^(Foreach variable arrayEntry statement)
      //{ $value = new ForeachStatement($expression.value, $statement.value, $elsestatement.value);}
    | ^(While expression statement)    
    | ^(Do statement expression)
    | ^(Switch expression cases)
    | functionDefinition
    ;

simpleStatement returns[PhpStatement value]
    : ^(Echo (exps+=expression)+)
      {$value = new EchoOperator($exps);}
    | ^(Global (idents+=name)+)
      {$value = new GlobalOperator($idents);}
    | ^(Static variable atom)
    | ^(Break Integer?)
      {$value = new BreakStatement();}
    | ^(Continue Integer?)
      {$value = new ContinueStatement();}
    //| Goto^ UnquotedString
    | ^(Return expression?)
      {$value = new ReturnOperator($expression.value);}
    | ^(RequireOperator expression)
      {$value = new RequireOperator($expression.value);}
    | expression
      {$value = $expression.value;}
    ;


forInit returns[PhpExpression value]
    : ^(ForInit expression+)
    ;

forCondition returns[PhpExpression value]
    : ^(ForCondition expression+)
    ;
    
forUpdate returns[PhpExpression value]
    : ^(ForUpdate expression+)
    ;

cases 
    : casestatement*  defaultcase
    ;

casestatement
    : ^(Case expression statement*)
    ;

defaultcase 
    : ^(Default statement*)
    ;

functionDefinition returns[PhpStatement value]
    : ^(Function UnquotedString ^(Params paramDef*) ^(Block statement*))
    ;
    
paramDef
    : ^(Equals paramName atom) 
    | paramName
    ;

paramName
    : ^(Dollar UnquotedString)
    | ^(Ampersand ^(Dollar UnquotedString))
    ;
    
expression returns[PhpExpression value]
    : ^(Or l=expression r=expression)
      { $value = new OrOperator($Or.text, $l.value, $r.value);}
    | ^(Xor l=expression r=expression)
      { $value = new XorOperator($Or.text, $l.value, $r.value);}
    | ^(And l=expression r=expression)
    | ^(Equals l=expression r=expression)
    | ^(AssignmentOperator l=expression r=expression)
    | ^(IfExpression expression expression expression)
    | ^(LogicalOr l=expression r=expression)
    | ^(LogicalAnd l=expression r=expression)
    | ^(Pipe l=expression r=expression)
    | ^(Ampersand l=expression r=expression)
    | ^(EqualityOperator l=expression r=expression)
    | ^(ShiftOperator l=expression r=expression)
    | ^(Plus l=expression r=expression)
    | ^(Minus l=expression r=expression)
    | ^(Dot l=expression r=expression)
    | ^(Asterisk l=expression r=expression)
    | ^(Forwardslash l=expression r=expression)
    | ^(Percent l=expression r=expression)
    | ^(Bang expression)
    | ^(Instanceof l=expression r=expression)
    | ^(Tilde expression)
    | ^(Minus expression) 
    | ^(SuppressWarnings expression)
    | ^(Cast PrimitiveType expression)
    | ^(Prefix IncrementOperator name)
    | ^(Postfix IncrementOperator name)
    | ^(New nameOrFunctionCall)
    | ^(Clone name)
    | atomOrReference
    ;


atomOrReference
    : atom
    | reference
    ;

arrayDeclaration
    : ^(Array arrayEntry*)
    ;

arrayEntry
    : (keyValuePair | expression)
    ;

keyValuePair
    : ^(ArrayAssign expression+)
    ;

atom: SingleQuotedString | DoubleQuotedString | HereDoc | Integer | Real | Boolean | arrayDeclaration
    ;

//Need to be smarter with references, they have their own tower of application.
reference
    : ^(Ampersand nameOrFunctionCall)
    | nameOrFunctionCall
    ;

nameOrFunctionCall
    : ^(Apply name expression*)
    | name
    ;

name: staticMemberAccess
    | memberAccess
    | variable
    ;
    
staticMemberAccess
    : ^('::' UnquotedString  variable)
    ;

memberAccess
    : ^(OpenSquareBrace variable expression)
    | ^('->' variable UnquotedString)
    ;
    
variable
    : ^(Dollar variable)
    | UnquotedString
    ;